package storage

import (
	"context"
	"fmt"
	"io"
	"path/filepath"
	"soffice/config"
	"strings"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

// MinIOStorage MinIO对象存储实现
type MinIOStorage struct {
	client     *minio.Client
	bucketName string
	endpoint   string
	useSSL     bool
}

// NewMinIOStorage 创建MinIO存储实例
func NewMinIOStorage(config Config) (*MinIOStorage, error) {
	endpoint := config.MinIOEndpoint
	if config.MinIOPort != "" {
		endpoint = fmt.Sprintf("%s:%s", endpoint, config.MinIOPort)
	}

	// 移除协议前缀
	endpoint = strings.TrimPrefix(endpoint, "http://")
	endpoint = strings.TrimPrefix(endpoint, "https://")

	// 初始化MinIO客户端
	client, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(config.MinIOAccessKey, config.MinIOSecretKey, ""),
		Secure: config.MinIOUseSSL,
	})
	if err != nil {
		return nil, fmt.Errorf("初始化MinIO客户端失败: %v", err)
	}

	// 检查bucket是否存在，不存在则创建
	ctx := context.Background()
	exists, err := client.BucketExists(ctx, config.MinioBucketName)
	if err != nil {
		return nil, fmt.Errorf("检查bucket失败: %v", err)
	}

	if !exists {
		err = client.MakeBucket(ctx, config.MinioBucketName, minio.MakeBucketOptions{})
		if err != nil {
			return nil, fmt.Errorf("创建bucket失败: %v", err)
		}
	}

	return &MinIOStorage{
		client:     client,
		bucketName: config.MinioBucketName,
		endpoint:   endpoint,
		useSSL:     config.MinIOUseSSL,
	}, nil
}

// SaveFile 保存文件到MinIO
func (s *MinIOStorage) SaveFile(data io.Reader, filename string, contentType string) (string, error) {
	ctx := context.Background()

	// 标准化路径分隔符为 /
	filename = filepath.ToSlash(filename)

	// 如果没有指定contentType，使用默认值
	if contentType == "" {
		contentType = getContentType(filename)
	}

	// 上传文件
	_, err := s.client.PutObject(ctx, s.bucketName, filename, data, -1, minio.PutObjectOptions{
		ContentType: contentType,
	})
	if err != nil {
		return "", fmt.Errorf("上传文件到MinIO失败: %v", err)
	}

	return s.GetFileURL(filename), nil
}

// GetFile 从MinIO获取文件
func (s *MinIOStorage) GetFile(filename string) (io.ReadCloser, error) {
	ctx := context.Background()
	filename = filepath.ToSlash(filename)

	object, err := s.client.GetObject(ctx, s.bucketName, filename, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("从MinIO获取文件失败: %v", err)
	}

	return object, nil
}

// DeleteFile 从MinIO删除文件
func (s *MinIOStorage) DeleteFile(filename string) error {
	ctx := context.Background()
	filename = filepath.ToSlash(filename)

	err := s.client.RemoveObject(ctx, s.bucketName, filename, minio.RemoveObjectOptions{})
	if err != nil {
		return fmt.Errorf("从MinIO删除文件失败: %v", err)
	}

	return nil
}

// GetFileURL 获取MinIO文件访问URL（通过当前服务转发）
func (s *MinIOStorage) GetFileURL(filename string) string {
	filename = filepath.ToSlash(filename)
	// 获取动态API基础路径
	apiBasePath := config.GetAPIBasePath()
	// 返回通过当前服务转发的下载地址
	return fmt.Sprintf("/%s/download-file/%s", apiBasePath, filename)
}

// FileExists 检查MinIO中文件是否存在
func (s *MinIOStorage) FileExists(filename string) (bool, error) {
	ctx := context.Background()
	filename = filepath.ToSlash(filename)

	_, err := s.client.StatObject(ctx, s.bucketName, filename, minio.StatObjectOptions{})
	if err != nil {
		errResponse := minio.ToErrorResponse(err)
		if errResponse.Code == "NoSuchKey" {
			return false, nil
		}
		return false, err
	}

	return true, nil
}

// getContentType 根据文件扩展名获取Content-Type
func getContentType(filename string) string {
	ext := strings.ToLower(filepath.Ext(filename))
	switch ext {
	case ".jpg", ".jpeg":
		return "image/jpeg"
	case ".png":
		return "image/png"
	case ".gif":
		return "image/gif"
	case ".webp":
		return "image/webp"
	case ".svg":
		return "image/svg+xml"
	case ".pdf":
		return "application/pdf"
	case ".html":
		return "text/html"
	case ".css":
		return "text/css"
	case ".js":
		return "application/javascript"
	default:
		return "application/octet-stream"
	}
}
